#include "Tableau.h"

Tableau::Tableau(Team* owningTeam) {
	this->owningTeam = owningTeam;
	this->winDistance = 1000; // Let's say default value. Can be set/"extended" during gameplay
}

Tableau::~Tableau() {
	this->owningTeam = NULL;
}




/**
 * The number of 200km already played.
 */
int Tableau::numberOf200() const {
	return distanceArea.count(Card::CardID_Distance_200);
}

/**
 * The distance already played.
 */
int Tableau::distance() const {
	return (200 * distanceArea.count(Card::CardID_Distance_200))
			+ (100 * distanceArea.count(Card::CardID_Distance_100))
			+ (75 * distanceArea.count(Card::CardID_Distance_75))
			+ (50 * distanceArea.count(Card::CardID_Distance_50))
			+ (25 * distanceArea.count(Card::CardID_Distance_25));
}


/**
 * Calculates whether or not we can roll on this tableau.
 */
bool Tableau::canRoll() const {
	
	CardID topBattle = battlePile.isEmpty() ? Card::CardID_NoCard : battlePile.last();
	
	
	// Roll if top is Roll card.
	if (topBattle == Card::CardID_Remedy_Roll)
		return true;
	
	
	/* Without a Roll, all other rules need the Right Of Way */
	
	if (!safetyArea.contains(Card::CardID_Safety_RightOfWay))
		return false;
	
	
	// Roll if battle pile empty (+ Right of Way)
	if (topBattle == Card::CardID_NoCard)
		return true;
	
	// Roll if top is a Remedy (+ Right of Way)
	if (Card::getInfo(topBattle)->getFunction() == CardInfo::Function_Remedy)
		return true;
	
	// Roll if top is a Hazard and we have the Safety (+ Right of Way)
	if ((Card::getInfo(topBattle)->getFunction() == CardInfo::Function_Hazard)
		&& safetyArea.contains(Card::cardForProperties(CardInfo::Function_Safety, Card::getInfo(topBattle)->getNature())))
		return true;
	
	
	// If everything else fails.
	return false;
}

/**
 * Using the canRoll() method, calculates the max value we can roll at a time.
 * This might be limited by: the amount of 200 played, a speed limit, or the winDistance
 */
int Tableau::canRollDistance() const {
	
	if (!canRoll()) return 0;
	
	// Let's juste progressively reduce the distance.
	int distance = 200;
	
	
	// Max of 200 km = 2.
	if (numberOf200() >= 2) distance = 100;
	
	// If there is a speed limit
	if (!speedPile.isEmpty() && speedPile.last() == Card::CardID_Hazard_SpeedLimit)
		distance = 50;
	
	
	// If we're still over winDistance, limiting to it.
	int addedDistance = this->distance() + distance;
	if (addedDistance > winDistance) {
		int excess = addedDistance - winDistance;
		distance = qMax(distance - excess, 0);
	}
	
	return distance;
}






/**
 * One of the core functions of the engine:
 * determine whether a player's card can be played on this tableau.
 */
bool Tableau::canPlay(Player* player, CardID card) const {
	
	// No valid card to play.
	if (card == Card::CardID_NoCard) return false;
	
	
	// Gathering information we need
	CardInfo* info = Card::getInfo(card); // card info
	bool allyPlayer = owningTeam->contains(player); // is this the player's tableau?
	
	
	
	//CardID safety = Card::CardID_NoCard; // the safety corresponding to the nature of the card
	
	
	switch (info->getFunction()) {
		
		
		
		
		
		
		/* Distance card */
	case CardInfo::Function_Distance:
		
		// Play only on our team's tableau.
		if (!allyPlayer)
			return false;
		
		/* Play only if the card is within the "canRoll" distance. This include: 
		- checking if we can roll (Roll card of with safeties),
		- checking the 200km cards,
		- checking speed limits,
		- checking whether the winDistance is exceeded.
		 */
		qDebug() << "lol" << canRollDistance();
		return (info->getDistance() <= canRollDistance());
		
		break;
		
		
		
		
		
		
		/* Hazard card */
	case CardInfo::Function_Hazard:
		
		// Play only on enemy tableau
		if (allyPlayer)
			return false;
		
		// Cannot play if the corresponding safety is there
		//safety = Card::cardForProperties(CardInfo::Function_Safety, info->getNature());
		if (safetyArea.keys().contains( Card::cardForProperties(CardInfo::Function_Safety, info->getNature()) ))
			return false;
		
		
		// Speed limit: empty pile or top=remedy
		if (info->getNature() == CardInfo::Nature_Speed) {
			return (speedPile.isEmpty() || speedPile.last() == Card::CardID_Remedy_EndOfLimit);
		}
		
		// Hazard: only if enemy can roll
		else {
			return canRoll();
		}
		
		break;
		
		
		
		
		
		
		
		
		/* Remedy cards */
	case CardInfo::Function_Remedy:
		
		// Play only on our team's tableau.
		if (!allyPlayer)
			return false;
		
		// Cannot play if the corresponding safety is there (well, useless at least)
		if (safetyArea.keys().contains( Card::cardForProperties(CardInfo::Function_Safety, info->getNature()) ))
			return false;
		
		
		// End of limit: !empty pile and top=limit
		if (info->getNature() == CardInfo::Nature_Speed) {
			return (!speedPile.isEmpty() && speedPile.last() == Card::CardID_Hazard_SpeedLimit);
		}
		
		// Remedy: only if the corresponding hazard is there
		else {
			
			// Special case of Roll: can play if there is no card, or a remedy (or Stop, but handled normally after)
			if ((card == Card::CardID_Remedy_Roll)
				&& (battlePile.isEmpty() || Card::getInfo(battlePile.last())->getFunction() == CardInfo::Function_Remedy))
				return true;
			
			return (!battlePile.isEmpty() && (battlePile.last() == Card::cardForProperties(CardInfo::Function_Hazard, info->getNature())));
		}
		
		break;
		
		
		
		
		
		
		
		/* Safety card */
	case CardInfo::Function_Safety:
		
		// As long as it's our tableau, we can play anytime.
		return allyPlayer;
		break;
		
	default:
		return false;
		break;
	}
	
	return false;
}




QList<CardID> Tableau::play(CardID card, bool coupFourre) {
	QList<CardID> discard;
	CardInfo* info = Card::getInfo(card);
	
	if (info == NULL) return discard;
	
	
	
	switch (info->getFunction()) {
		
		// Distance cards, added to the list
		case CardInfo::Function_Distance:
			distanceArea += card;
			break;
			
		// Hazards and Remedy, added to the battle or speed list (depends)
		case CardInfo::Function_Hazard:
		case CardInfo::Function_Remedy:
			
			if (info->getNature() == CardInfo::Nature_Speed)
				speedPile += card;
			else
				battlePile += card;
			
			break;
		
		// Safety cards in the safety area
		case CardInfo::Function_Safety:
			safetyArea[card] = coupFourre;
			
			if (coupFourre) {
				if ((card == Card::CardID_Safety_RightOfWay) && (speedPile.size() > 0) && (speedPile.last() == Card::CardID_Hazard_SpeedLimit))
					discard += speedPile.takeLast();
				
				if ((battlePile.size() > 0) && (Card::getInfo(battlePile.last())->getFunction() & CardInfo::Function_Hazard))
					discard += battlePile.takeLast();
			}
			
			break;
			
		default:
			break;
	}
	
	
	return discard;
}

//int Tableau::points();











//QDebug operator<<(QDebug dbg, const Tableau &tab);
QDebug operator<<(QDebug dbg, const Tableau &tab) {
	
	dbg.nospace() <<
			"Battle: " << tab.battlePile <<
			" / Speed: " << tab.speedPile <<
			" / Distance [" + QString::number(tab.numberOf200()) + "/" + QString::number(tab.distance()) + "]: " << tab.distanceArea <<
			" / Safeties: " << tab.safetyArea;
	
	return dbg.space();
}
